/**
 * Created by merlin on 24/11/2018.
 */
// 需要手动安装的插件
const gulp = require('gulp');
const clean = require('gulp-clean'); //清理目录
const rev = require('gulp-rev'); //更改版本名
const csso = require('gulp-csso'); //压缩css
const uglify = require('gulp-uglify'); //压缩js
const rename = require('gulp-rename'); //重命名
const jshint = require('gulp-jshint'); //js语法检查
const revFormat = require('gulp-rev-format');
const revReplace = require('gulp-rev-replace');
const prefix = require('gulp-prefix');
const sequence = require('gulp-sequence');
const htmlmin = require('gulp-htmlmin');
const oss = require('ali-oss');
const through = require('through2');
const map = require('map-stream');
const urlPrefixer = require('gulp-css-url-prefixer');
// 不需要手动安装的插件
const fs = require('fs');
const path = require('path');
// 与项目无关的常量
const CURRENT_PATH = './';
const DIST_ROOT = 'dist';
const STATIC_DIR = 'static';
const HTML_DIR = 'html';
const REV_DIR = 'rev';
const BUILD_DIR = 'build';
const DIRECTORY_SEPARATOR = '/';
const REV_MANIFEST_JSON_FILE = 'rev-manifest.json';
const REV_UPLOAD_OSS_FILE = 'rev-upload-oss.json';
const BUILD_RESULT_FILE = 'build.result';
const PROJECTS = {
    'WEB' : 'web', 'AGENT' : 'agent', 'CMS' : 'cms'
};
const PROFILES = {
    'PROD' : 'prod', 'TEST' : 'test'
};
const HTTP_STATUS_CODE = {
    'SUCCESS' : 200, 'FILE_NOT_FOUNDED' : 404
};
const BUILD_STEP = {
    'START' : 'start', 'FAIL' : 'fail', 'SUCCESS' : 'success'
};
const CHARSET = {
    'UTF8' : 'utf-8', 'GBK' : 'gbk'
};
const ALI_OSS_ERROR_CODE = {
    'NO_SUCH_KEY' : 'NoSuchKey'
};
// 与项目环境有关的常量
const OSS_CONFIGS = {
    'PROD' : {
        'REGION' : 'oss-cn-shenzhen',
        'ACCESS_KEY_ID' : 'LTAICoZhvzP3lVUp',
        'ACCESS_KEY_SECRET' : 'qU4DFUvUIyPybwqqCm67n9AWPjm3h9',
        'BUCKET' : 'huayan-static'
    },
    'TEST' : {
        'REGION' : 'oss-cn-shenzhen',
        'ACCESS_KEY_ID' : 'LTAICoZhvzP3lVUp',
        'ACCESS_KEY_SECRET' : 'qU4DFUvUIyPybwqqCm67n9AWPjm3h9',
        'BUCKET' : 'huayan-static-test'
    }
};
// 与项目有关的常量
const STATIC_PATH = {
    'WEB' : PROJECTS.WEB + DIRECTORY_SEPARATOR + STATIC_DIR,
    'AGENT' : PROJECTS.AGENT + DIRECTORY_SEPARATOR + STATIC_DIR,
    'CMS' : PROJECTS.CMS + DIRECTORY_SEPARATOR + STATIC_DIR
};
const HTML_PATH = {
    'WEB' : PROJECTS.WEB + DIRECTORY_SEPARATOR + HTML_DIR,
    'AGENT' : PROJECTS.AGENT + DIRECTORY_SEPARATOR + HTML_DIR,
    'CMS' : PROJECTS.CMS + DIRECTORY_SEPARATOR + HTML_DIR
};
const DIST_STATIC_PATH = {
    'WEB' : DIST_ROOT + DIRECTORY_SEPARATOR + PROJECTS.WEB + DIRECTORY_SEPARATOR + STATIC_DIR,
    'AGENT' : DIST_ROOT + DIRECTORY_SEPARATOR + PROJECTS.AGENT + DIRECTORY_SEPARATOR + STATIC_DIR,
    'CMS' : DIST_ROOT + DIRECTORY_SEPARATOR + PROJECTS.CMS + DIRECTORY_SEPARATOR + STATIC_DIR
};
const DIST_HTML_PATH = {
    'WEB' : DIST_ROOT + DIRECTORY_SEPARATOR + PROJECTS.WEB + DIRECTORY_SEPARATOR + HTML_DIR,
    'AGENT' : DIST_ROOT + DIRECTORY_SEPARATOR + PROJECTS.AGENT + DIRECTORY_SEPARATOR + HTML_DIR,
    'CMS' : DIST_ROOT + DIRECTORY_SEPARATOR + PROJECTS.CMS + DIRECTORY_SEPARATOR + HTML_DIR
};
const DIST_REV_PATH = {
    'WEB' : DIST_ROOT + DIRECTORY_SEPARATOR + REV_DIR + DIRECTORY_SEPARATOR + PROJECTS.WEB,
    'AGENT' : DIST_ROOT + DIRECTORY_SEPARATOR + REV_DIR + DIRECTORY_SEPARATOR + PROJECTS.AGENT,
    'CMS' : DIST_ROOT + DIRECTORY_SEPARATOR + REV_DIR + DIRECTORY_SEPARATOR + PROJECTS.CMS
};
const DIST_BUILD_PATH = {
    'WEB' : DIST_ROOT + DIRECTORY_SEPARATOR + BUILD_DIR + DIRECTORY_SEPARATOR + PROJECTS.WEB,
    'AGENT' : DIST_ROOT + DIRECTORY_SEPARATOR + BUILD_DIR + DIRECTORY_SEPARATOR + PROJECTS.AGENT,
    'CMS' : DIST_ROOT + DIRECTORY_SEPARATOR + BUILD_DIR + DIRECTORY_SEPARATOR + PROJECTS.CMS
};
const DIST_PATH = {
    'WEB' : DIST_ROOT + DIRECTORY_SEPARATOR + PROJECTS.WEB,
    'AGENT' : DIST_ROOT + DIRECTORY_SEPARATOR + PROJECTS.AGENT,
    'CMS' : DIST_ROOT + DIRECTORY_SEPARATOR + PROJECTS.CMS
};
const JshintReporter = map(function (file, cb) {
    if (file.jshint.success) {
        return cb(null, file);
    }
    console.log('JSHINT fail in', file.path);
    file.jshint.results.forEach(function (result) {
        if (!result.error) {
            return;
        }
        const err = result.error;
        console.log(`  line ${err.line}, col ${err.character}, code ${err.code}, ${err.reason}`);
        setBuildStep(BUILD_STEP.FAIL);
    });
    cb(null, file);
});
// 根据编译的项目生成的变量
var PROJECT = null;
var PROJECT_STATIC_PATH = null;
var PROJECT_HTML_PATH = null;
var PROJECT_DIST_STATIC_PATH = null;
var PROJECT_DIST_HTML_PATH = null;
var PROJECT_DIST_REV_PATH = null;
var PROJECT_DIST_PATH = null;
var PROJECT_DIST_BUILD_PATH = null;
// 根据编译的项目环境生成的变量
var OSS_CONFIG = null;
var CLIENT = null;
var OSS_PREFIX = null;
// 全局变量
var revUploadOssData = null;
var revManifestData = null;

// 设置项目
var setProject = function(project) {
    switch (project) {
        case PROJECTS.WEB :
            PROJECT = PROJECTS.WEB;
            PROJECT_STATIC_PATH = STATIC_PATH.WEB;
            PROJECT_HTML_PATH = HTML_PATH.WEB;
            PROJECT_DIST_STATIC_PATH = DIST_STATIC_PATH.WEB;
            PROJECT_DIST_HTML_PATH = DIST_HTML_PATH.WEB;
            PROJECT_DIST_REV_PATH = DIST_REV_PATH.WEB;
            PROJECT_DIST_PATH = DIST_PATH.WEB;
            PROJECT_DIST_BUILD_PATH = DIST_BUILD_PATH.WEB;
            break;
        case PROJECTS.CMS :
            PROJECT = PROJECTS.CMS;
            PROJECT_STATIC_PATH = STATIC_PATH.CMS;
            PROJECT_HTML_PATH = HTML_PATH.CMS;
            PROJECT_DIST_STATIC_PATH = DIST_STATIC_PATH.CMS;
            PROJECT_DIST_HTML_PATH = DIST_HTML_PATH.CMS;
            PROJECT_DIST_REV_PATH = DIST_REV_PATH.CMS;
            PROJECT_DIST_PATH = DIST_PATH.CMS;
            PROJECT_DIST_BUILD_PATH = DIST_BUILD_PATH.CMS;
            break;
        case PROJECTS.AGENT :
            PROJECT = PROJECTS.AGENT;
            PROJECT_STATIC_PATH = STATIC_PATH.AGENT;
            PROJECT_HTML_PATH = HTML_PATH.AGENT;
            PROJECT_DIST_STATIC_PATH = DIST_STATIC_PATH.AGENT;
            PROJECT_DIST_HTML_PATH = DIST_HTML_PATH.AGENT;
            PROJECT_DIST_REV_PATH = DIST_REV_PATH.AGENT;
            PROJECT_DIST_PATH = DIST_PATH.AGENT;
            PROJECT_DIST_BUILD_PATH = DIST_BUILD_PATH.AGENT;
            break;
        default :
            throw new Error('PROJECT NOT EXISTS, PLEASE CHECK YOUR PROJECT[ ' + project + ' ]');
            break;
    }
};
// 设置项目环境
var setProfile = function(profile) {
    switch (profile) {
        case PROFILES.PROD :
            OSS_CONFIG = OSS_CONFIGS.PROD;
            break;
        case PROFILES.TEST :
            OSS_CONFIG = OSS_CONFIGS.TEST;
            break;
        default :
            throw new Error('PROFILE NOT EXISTS, PLEASE CHECK YOUR PROFILE[ ' + project + ' ]');
            break;
    }
    CLIENT = new oss({
        region : OSS_CONFIG.REGION,
        accessKeyId : OSS_CONFIG.ACCESS_KEY_ID,
        accessKeySecret : OSS_CONFIG.ACCESS_KEY_SECRET,
        bucket : OSS_CONFIG.BUCKET
    });
    OSS_PREFIX = 'https://' + OSS_CONFIG.BUCKET + '.oss-cn-shenzhen.aliyuncs.com';
};
// 同步创建文件目录
function mkPathSync(dirPath) {
    // path.sep 文件路径分隔符（mac 与 window 不同）
    // 转变成数组，如 ['a', 'b', 'c']
    let parts = dirPath.split(path.sep);
    for(let i = 1; i <= parts.length; i++) {
        // 重新拼接成 a a/b a/b/c
        let current = parts.slice(0, i).join(path.sep);
        // accessSync 路径不存在则抛出错误在 catch 中创建文件夹
        try {
            fs.accessSync(current);
        } catch(e) {
            try{
                fs.mkdirSync(current);
            } catch (e) {
                console.log(e);
                setBuildStep(BUILD_STEP.FAIL);
            }

        }
    }
}
/**
 * 上传文件
 * @param file
 */
var doUpload = function (file) {
    info = fs.statSync(file.path);
    if(info.isDirectory()) {
        return;
    }
    CLIENT.put(PROJECT_STATIC_PATH + DIRECTORY_SEPARATOR + file.relative, file.path)
        .catch((e) => {
            console.log(e);
            setBuildStep(BUILD_STEP.FAIL);
        });
};
var doUploadByPath = function (ossPath, filePath) {
    CLIENT.put(ossPath, filePath)
        .catch((e) => {
            console.log(e);
            setBuildStep(BUILD_STEP.FAIL);
        });
};
var doUploadByBuffer = function (ossPath, buffer) {
    CLIENT.put(ossPath, buffer)
        .catch((e) => {
            console.log(e);
            setBuildStep(BUILD_STEP.FAIL);
        });
};
//设置编译进度
var setBuildStep = function (step) {
    mkPathSync(CURRENT_PATH + PROJECT_DIST_BUILD_PATH);
    var filePath = path.join(__dirname, PROJECT_DIST_BUILD_PATH, BUILD_RESULT_FILE);
    fs.writeFileSync(filePath, step, CHARSET.UTF8);
    if(step === BUILD_STEP.FAIL) {
        throw new Error("Build fail, Please fix and try again!");
    }
};
var getBuildStep = function () {
    var filePath = path.join(__dirname, PROJECT_DIST_BUILD_PATH, BUILD_RESULT_FILE);
    var buildResult = null;
    exist = fs.existsSync(filePath);
    if(exist) {
        buildResult = fs.readFileSync(filePath, CHARSET.UTF8);
    }
    return buildResult;
};

var modifyFilename = function(filename) {
    var _filename = filename.replace(/\.[\w]*\.cache/, "");
    //压缩时修改文件名
    // if(_filename.indexOf('.css') > -1 && _filename.indexOf('.min') === -1) {
    //     _filename = _filename.replace('\.css', '\.min\.css');
    // }
    // if(_filename.indexOf('.js') > -1 && _filename.indexOf('.min') === -1) {
    //     _filename = _filename.replace('\.js', '\.min\.js');
    // }
    return _filename;
};
var modifyUnreved = function(filename) {
    return filename;
};
var modifyReved = function(filename) {
    if (filename.indexOf('.cache') > -1) {
        const _version = filename.match(/\.[\w]*\.cache/)[0].replace(/(\.|cache)*/g, "");
        _filename = modifyFilename(filename);
        return _filename + "?v=" + _version;
    }
    return filename;
};

// 根据项目初始化
gulp.task('initProjectForWeb', function (cb) {
    setProject(PROJECTS.WEB);
    cb();
});
gulp.task('initProjectForCms', function (cb) {
    setProject(PROJECTS.CMS);
    cb();
});
gulp.task('initProjectForAgent', function (cb) {
    setProject(PROJECTS.AGENT);
    cb();
});
// 根据项目环境初始化
gulp.task('initProfileForProd', function (cb) {
    setProfile(PROFILES.PROD);
    cb();
});
gulp.task('initProfileForTest', function (cb) {
    setProfile(PROFILES.TEST);
    cb();
});
// 设置编译进度
gulp.task('setBuildStart', function (cb) {
    setBuildStep(BUILD_STEP.START);
    cb();
});
gulp.task('setBuildFinish', function (cb) {
    var buildResult = getBuildStep();
    if(buildResult === BUILD_STEP.START) {
        setBuildStep(BUILD_STEP.SUCCESS);
    }
    cb();
});
// 获取oss中已上传资源的版本信息
gulp.task('getRevUploadOssData', function(cb) {
    var ossPath = PROJECT + DIRECTORY_SEPARATOR + REV_UPLOAD_OSS_FILE;
    CLIENT.get(ossPath).then((result)=>{
        if(result.res.statusCode === HTTP_STATUS_CODE.SUCCESS) {
            revUploadOssData = JSON.parse(result.content.toString());
            cb();
        }
    }).catch((e)=>{
        if(e.code === ALI_OSS_ERROR_CODE.NO_SUCH_KEY) {
            console.log("ignoge error [" + e.code + "] " + e.message);
            cb();
            return;
        }
        console.log("error: " + e);
        setBuildStep(BUILD_STEP.FAIL);
        cb();
    });
});
// 上传静态资源到oss并更新oss上的资源版本信息
gulp.task('upload2Oss', function(cb){
    var revManifestFile = path.join(__dirname, PROJECT_DIST_REV_PATH, REV_MANIFEST_JSON_FILE);
    revManifestData = JSON.parse(fs.readFileSync(revManifestFile,CHARSET.UTF8));
    if(revUploadOssData == null) {
        // 全量上传
        return gulp.src(PROJECT_DIST_STATIC_PATH + '/**')
            .pipe(through.obj(function(file,enc,cb){
                doUpload(file);
                doUploadByBuffer(PROJECT + DIRECTORY_SEPARATOR + REV_UPLOAD_OSS_FILE, Buffer.from(JSON.stringify(revManifestData)));
                cb();
            }));
    } else {
        // 增量上传
        for(var key in revManifestData) {
            if(revUploadOssData[key] === undefined || revUploadOssData[key] != revManifestData[key]) {
                var filename = revManifestData[key];
                console.log('undefined/changed [new: ' + filename + '][old: ]' + revUploadOssData[key]);
                var _filename = modifyFilename(filename);
                doUploadByPath(PROJECT_STATIC_PATH + DIRECTORY_SEPARATOR + _filename, path.join(__dirname, PROJECT_DIST_STATIC_PATH, _filename));
                continue;
            }
        }
        doUploadByBuffer(PROJECT + DIRECTORY_SEPARATOR + REV_UPLOAD_OSS_FILE, Buffer.from(JSON.stringify(revManifestData)));
        cb();
    }
});

// 清理构建结果
gulp.task('clean', function(){
    return gulp.src([CURRENT_PATH + PROJECT_DIST_PATH, CURRENT_PATH + PROJECT_DIST_REV_PATH, CURRENT_PATH + PROJECT_DIST_BUILD_PATH], {read : false})
        .pipe(clean());
});

// 复制html及静态资源文件到目标目录
gulp.task('copyhtml', function () {
    return gulp.src([CURRENT_PATH + PROJECT + '/**/*.html', CURRENT_PATH + PROJECT + '/**/*.htm'])
        .pipe(gulp.dest(CURRENT_PATH + PROJECT_DIST_PATH));
});
gulp.task('copyresources', function () {
        return gulp.src(CURRENT_PATH + PROJECT_STATIC_PATH + '/**')
        .pipe(gulp.dest(CURRENT_PATH + PROJECT_DIST_STATIC_PATH));
});
gulp.task('copyothers', function () {
    return gulp.src([CURRENT_PATH + PROJECT + '/*', '!' + CURRENT_PATH + PROJECT + '/html', '!' + CURRENT_PATH + PROJECT + '/static'])
        .pipe(gulp.dest(CURRENT_PATH + PROJECT_DIST_PATH));
});

// 生成静态资源文件的最新版本信息
gulp.task('rev', function () {
    return gulp.src(CURRENT_PATH + PROJECT_DIST_STATIC_PATH + '/**')
        .pipe(rev())
        .pipe(revFormat({
            prefix: '.', // 在版本号前增加字符
            suffix: '.cache', // 在版本号后增加字符
            lastExt: false
        }))
        .pipe(rev.manifest())
        .pipe(gulp.dest(CURRENT_PATH + PROJECT_DIST_REV_PATH));
});

// 为 html 里的静态资源文件链接增加版本号
gulp.task('add-version', function() {
    var manifest = gulp.src([CURRENT_PATH + PROJECT_DIST_REV_PATH + DIRECTORY_SEPARATOR + REV_MANIFEST_JSON_FILE]);
    return gulp.src([CURRENT_PATH + PROJECT_DIST_PATH + '/**/*.html'])
        .pipe(revReplace({
            manifest: manifest,
            modifyUnreved: modifyUnreved,
            modifyReved: modifyReved
        }))
        .pipe(gulp.dest(CURRENT_PATH + PROJECT_DIST_PATH));
});

// 压缩
gulp.task('htmlcompress', function () {
    return;
    return gulp.src(CURRENT_PATH + PROJECT_DIST_PATH + '/**/*.html')
        .pipe(htmlmin({ collapseWhitespace: true }))
        .pipe(gulp.dest(CURRENT_PATH + PROJECT_DIST_PATH));
});
gulp.task('csscompress', function () {
    return;
    return gulp.src(CURRENT_PATH + PROJECT_DIST_PATH + '/**/*.css')
        .pipe(clean())
        .pipe(csso())
        .pipe(rename({suffix : '.min'}))
        .pipe(gulp.dest(CURRENT_PATH + PROJECT_DIST_PATH));
});
gulp.task('jscompress', function () {
    return;
    return gulp.src(CURRENT_PATH + PROJECT_DIST_PATH + '/**/*.js')
        .pipe(clean())
        .pipe(uglify())
        .pipe(rename({suffix : '.min'}))
        .pipe(gulp.dest(CURRENT_PATH + PROJECT_DIST_PATH));
});

// 检查语法
gulp.task('jshint', function () {
    return;
    return gulp.src(CURRENT_PATH + PROJECT_STATIC_PATH + '/**/*.js')
        .pipe(jshint({
            'asi' : true, // 允许省略分号
            'expr' : true, //允许__iterator__;不是所有的浏览器都支持__iterator__
        }))
        .pipe(JshintReporter);
});

// 为 html 里的静态资源文件链接加上 oss 前缀
gulp.task('add-prefix', function () {
    return gulp.src(CURRENT_PATH + PROJECT_DIST_PATH + '/**/*.html')
        .pipe(prefix(OSS_PREFIX + DIRECTORY_SEPARATOR + PROJECT, null, ['http', '//']))
        .pipe(gulp.dest(CURRENT_PATH + PROJECT_DIST_PATH));
});

//用于对css中使用的url进行重写,如用到CDN的时候重新构造新的域名和路径
gulp.task("add-prefix-css",function(){
    return gulp.src(CURRENT_PATH + PROJECT_DIST_PATH + '/**/*.css')
        .pipe(urlPrefixer(OSS_PREFIX + DIRECTORY_SEPARATOR + PROJECT))
        .pipe(gulp.dest(CURRENT_PATH + PROJECT_DIST_PATH));

});

gulp.task('test', sequence('initWeb', 'clean'));

gulp.task('build', sequence('clean', 'setBuildStart', 'jshint', ['copyhtml', 'copyresources', 'copyothers'], 'rev', ['htmlcompress', 'csscompress', 'jscompress'], 'add-version', 'add-prefix-css', 'getRevUploadOssData', 'upload2Oss', 'add-prefix', 'setBuildFinish'));
gulp.task('build-web-prod', sequence('initProjectForWeb', 'initProfileForProd', 'build'));
gulp.task('build-cms-prod', sequence('initProjectForCms', 'initProfileForProd', 'build'));
gulp.task('build-agent-prod', sequence('initProjectForAgent', 'initProfileForProd', 'build'));
gulp.task('build-web-test', sequence('initProjectForWeb', 'initProfileForTest', 'build'));
gulp.task('build-cms-test', sequence('initProjectForCms', 'initProfileForTest', 'build'));
gulp.task('build-agent-test', sequence('initProjectForAgent', 'initProfileForTest', 'build'));

